home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 …ember: Reference Library / Dev.CD Dec 00 RL Disk 1.toast / mac / Technical Documentation / Develop / develop Issue 24 / develop Issue 24 code / Scriptable Database 1.0a15.sea / Scriptable Database 1.0a15 / Database / DBProperty.cp / DBProperty.cp
Encoding:
Text File  |  1996-04-29  |  17.6 KB  |  543 lines  |  [TEXT/CWIE]

  1. //================================================================================
  2. // Greg Anderson
  3. // db+
  4. //
  5. // Cursor to a data record
  6. // 18 May 1994
  7. // 31 Dec 1994
  8. //================================================================================
  9. #pragma once
  10.  
  11. #include "DBProperty.h"
  12.  
  13. #include "DatabaseDocument.h"
  14. #include "DataRecord.h"
  15. #include "Transaction.h"
  16.  
  17. #include "Exceptions.h"
  18.  
  19. #define INHERITED TDBRecord
  20.  
  21. //--------------------------------------------------------------------------------
  22. // TDBProperty::~TDBProperty
  23. //--------------------------------------------------------------------------------
  24. TDBProperty::~TDBProperty()
  25. {
  26. } // TDBProperty::~TDBProperty
  27.  
  28. //--------------------------------------------------------------------------------
  29. // TDBProperty::CompareSortKeys
  30. //--------------------------------------------------------------------------------
  31. CompareEnumeration TDBProperty::CompareSortKeys(TTransaction* t, AConst<TDBRecord> secondObject) const
  32. {
  33.     AConst<TDBProperty> dataRecord = secondObject->DBPropertyCursor();
  34.     
  35.     return this->DetermineCompareEnumeration(this->PropertyID(t), dataRecord->PropertyID(t));
  36. } // TDBProperty::CompareSortKeys
  37.  
  38. //--------------------------------------------------------------------------------
  39. // TDBProperty::GetDataType
  40. //--------------------------------------------------------------------------------
  41. long TDBProperty::GetDataType(TTransaction* t) const
  42. {
  43.     long theDataType = kNullType;
  44.     
  45.     switch(this->GetEncodedDataType(t))
  46.     {
  47.         //
  48.         // •To do: Have a table of well-known data types,
  49.         // and do a lookup
  50.         //
  51.         case kEncodedNullType:
  52.             theDataType = kNullType;
  53.             break;
  54.             
  55.         case kEncodedSmallStringType:
  56.             theDataType = kStringType;
  57.             break;
  58.         
  59.         //
  60.         // •To do:  Store these types when the properties/elements
  61.         // are stored in the property record, then remove this
  62.         // special-case code and use case kEncodedTypedStructure instead.
  63.         //
  64.         case kSetOfProperties:
  65.             theDataType = kRecordType;
  66.             break;
  67.  
  68.         case kSetOfElements:
  69.             theDataType = kRecordType;
  70.             break;
  71.             
  72.         case kEncodedTypedStructure:
  73.         default:
  74.             theDataType = this->GetStoredDataType(t);
  75.             break;
  76.  
  77.     }
  78.     
  79.     return theDataType;
  80. } // TDBProperty::GetDataType
  81.  
  82. //--------------------------------------------------------------------------------
  83. // TDBProperty::GetDataLength
  84. //--------------------------------------------------------------------------------
  85. long TDBProperty::GetDataLength(TTransaction* t) const
  86. {
  87.     long theDataLength = 0;
  88.     
  89.     switch(this->GetEncodedDataType(t))
  90.     {
  91.         case kEncodedNullType:
  92.             theDataLength = 0;
  93.             break;
  94.  
  95.         case kSetOfProperties:
  96.         case kSetOfElements:
  97.             FailErr(eWrongDataType);
  98.             break;
  99.             
  100.         case kEncodedSmallStringType:
  101.         case kEncodedTypedStructure:
  102.         {
  103.             if(this->OwnsExternalData(t) == false)
  104.             {
  105.                 theDataLength = this->GetInternalDataLength(t);
  106.             }
  107.             else
  108.             {
  109.                 //
  110.                 // Ask external data record how much data it holds
  111.                 //
  112.                 AConst<TDataRecord> externalData = this->ExternalData(t);
  113.                 Require(externalData.Exists());
  114.                 theDataLength = externalData->GetDataLength(t);
  115.             }
  116.             break;
  117.         }
  118.     }
  119.     
  120.     return theDataLength;
  121. } // TDBProperty::GetDataLength
  122.  
  123. //--------------------------------------------------------------------------------
  124. // TDBProperty::GetLongData
  125. //--------------------------------------------------------------------------------
  126. long TDBProperty::GetLongData(TTransaction* t) const
  127. {
  128.     Require(this->GetDataType(t) == kLongType);
  129.     return this->GetRecordData(t, kFirstDataWord);
  130. } // TDBProperty::GetLongData
  131.  
  132. //--------------------------------------------------------------------------------
  133. // TDBProperty::GetTypedData
  134. //--------------------------------------------------------------------------------
  135. void TDBProperty::GetTypedData(TTransaction* t, TAbstractDataReference& data) const
  136. {
  137.     if(this->OwnsExternalData(t) == false)
  138.     {
  139.         //
  140.         // First, copy the data we're only allowed to access a longword
  141.         // at a time into some other buffer
  142.         //
  143.         long buffer[3];
  144.         
  145.         for(short i=0; i<3; ++i)
  146.             buffer[i] = this->GetRecordData(t, kFirstDataWord+i);
  147.         
  148.         //
  149.         // Next, copy one char at a time from that buffer into the
  150.         // caller's data space.
  151.         //
  152.         long lengthToCopy = (this->GetDataLength(t) < data.MaxLength()) ? this->GetDataLength(t) : data.MaxLength();
  153.         data.CopyFrom(TConstDataReference(this->GetDataType(t), (char*)&buffer[0], lengthToCopy), true);
  154.     }
  155.     else
  156.     {
  157.         //
  158.         // Copy the data from the external data record
  159.         //
  160.         AConst<TDataRecord> externalData = this->ExternalData(t);
  161.         externalData->GetTypedData(t, data);
  162.     }
  163. } // TDBProperty::GetTypedData
  164.  
  165. //--------------------------------------------------------------------------------
  166. // TDBProperty::Compare
  167. //--------------------------------------------------------------------------------
  168. CompareEnumeration TDBProperty::Compare(TTransaction* t, const TAbstractDataReference& compareWith) const
  169. {
  170.     CompareEnumeration compareResult;
  171.     
  172.     if(this->OwnsExternalData(t) == false)
  173.     {
  174.         compareResult = this->CompareRecordData(t, this->GetDataType(t), kFirstDataWord, this->GetDataLength(t), compareWith);
  175.     }
  176.     else
  177.     {
  178.         //
  179.         // We don't need the rest of this method--just these two lines
  180.         //
  181.         TConstDataReference thisDataRef = this->PropertyDataReference(t);
  182.         compareResult = thisDataRef.Compare(compareWith);
  183.     }
  184.     
  185.     return compareResult;
  186. } // TDBProperty::Compare
  187.  
  188. //--------------------------------------------------------------------------------
  189. // TDBProperty::Compare
  190. //--------------------------------------------------------------------------------
  191. CompareEnumeration TDBProperty::Compare(TTransaction* t, AConst<TDBProperty> compareWith) const
  192. {
  193.     return this->Compare(t, compareWith->PropertyDataReference(t));
  194. } // TDBProperty::Compare
  195.  
  196. //--------------------------------------------------------------------------------
  197. // TDBProperty::Contains
  198. //--------------------------------------------------------------------------------
  199. Boolean TDBProperty::Contains(TTransaction* t, const TAbstractDataReference& searchFor) const
  200. {
  201.     Boolean doesContain = false;
  202.     
  203.     if(this->OwnsExternalData(t) == false)
  204.     {
  205.         doesContain = this->RecordDataContains(t, this->GetDataType(t), kFirstDataWord, this->GetDataLength(t), searchFor);
  206.     }
  207.     else
  208.     {
  209.         //
  210.         // We don't need the rest of this method--just these two lines
  211.         //
  212.         TConstDataReference thisDataRef = this->PropertyDataReference(t);
  213.         doesContain = thisDataRef.Contains(searchFor);
  214.     }
  215.     
  216.     return doesContain;
  217. } // TDBProperty::Contains
  218.  
  219. //--------------------------------------------------------------------------------
  220. // TDBProperty::Contains
  221. //--------------------------------------------------------------------------------
  222. Boolean TDBProperty::Contains(TTransaction* t, AConst<TDBProperty> compareWith) const
  223. {
  224.     return this->Contains(t, compareWith->PropertyDataReference(t));
  225. } // TDBProperty::Contains
  226.  
  227. //--------------------------------------------------------------------------------
  228. // TDBProperty::FreeOwnedData
  229. //--------------------------------------------------------------------------------
  230. void TDBProperty::FreeOwnedData(TTransaction* t)
  231. {
  232.     //
  233.     // We only need to worry about freeing owned external
  234.     // data; elements and properties will be freed up by
  235.     // INHERITED::FreeOwnedData.
  236.     //
  237.     if(this->OwnsExternalData(t))
  238.     {
  239.         //
  240.         // Free our owned external data
  241.         //
  242.         AnUpdate<TDataRecord> externalData = this->UpdateExternalData(t);
  243.         if(externalData.Exists())
  244.             externalData->ReleaseData(t);
  245.         else
  246.             ASSERT(false);
  247.         this->SetOwnsExternalData(t, false);
  248.     }
  249.     
  250.     INHERITED::FreeOwnedData(t);
  251. } // TDBProperty::FreeOwnedData
  252.  
  253. //--------------------------------------------------------------------------------
  254. // TDBProperty::InitializeNewRecord
  255. //--------------------------------------------------------------------------------
  256. void TDBProperty::InitializeNewRecord(TTransaction* t)
  257. {
  258.     TDBRecord::InitializeNewRecord(t);
  259.  
  260.     this->SetRecordFlags(t, kInitialDataRecordFlags);
  261.     this->SetPropertyID(t, 0);
  262.     this->ChangeRecordData(t, kFirstDataWord, 0);
  263.     this->ChangeRecordData(t, kSecondDataWord, 0);
  264.     this->ChangeRecordData(t, kThirdDataWord, 0);
  265. } // TDBProperty::InitializeNewRecord
  266.  
  267. //--------------------------------------------------------------------------------
  268. // TDBProperty::MakeEmpty
  269. //--------------------------------------------------------------------------------
  270. void TDBProperty::MakeEmpty(TTransaction* t)
  271. {
  272.     this->FreeOwnedData(t);
  273.     this->SetEncodedDataType(t, kEncodedNullType);
  274.     this->SetInternalDataLength(t, 0);
  275.     this->ChangeRecordData(t, kFirstDataWord, 0);
  276.     this->ChangeRecordData(t, kSecondDataWord, 0);
  277.     this->ChangeRecordData(t, kThirdDataWord, 0);
  278.     this->InformOwnerPropertyValueChanged(t);
  279. } // TDBProperty::MakeEmpty
  280.  
  281. //--------------------------------------------------------------------------------
  282. // TDBProperty::SetLongData
  283. //--------------------------------------------------------------------------------
  284. void TDBProperty::SetLongData(TTransaction* t, long newValue)
  285. {
  286.     this->SetTypedData(t, TConstDataReference(kLongType, (char*)&newValue, sizeof(long))); 
  287. } // TDBProperty::SetLongData
  288.  
  289. //--------------------------------------------------------------------------------
  290. // TDBProperty::SetTypedData
  291. //--------------------------------------------------------------------------------
  292. void TDBProperty::SetTypedData(TTransaction* t, const TAbstractDataReference& data)
  293. {
  294.     long dataType = data.DataType();
  295.     long dataLength = data.DataLength();
  296.     
  297.     //
  298.     // •To do:  Look for 'dataLength' in a table of well-known
  299.     // types, and set 'maxInternalLength' to 12 if it is found.
  300.     //
  301.     // n.b. kEncodedTypedStructure is the correct encoded type
  302.     // to use for data > 12 bytes in length, even if the type is
  303.     // well-known.
  304.     //
  305.     long encodedDataType = ((dataType == kStringType) ? kEncodedSmallStringType : kEncodedTypedStructure);
  306.     long maxInternalLength = (encodedDataType != kEncodedTypedStructure) ? 12 : 8;
  307.     
  308.     if(dataLength <= maxInternalLength)
  309.     {
  310.         //
  311.         // Free the data that we used to own, if any 
  312.         //
  313.         this->FreeOwnedData(t);
  314.         
  315.         //
  316.         // Remember the length of the data and its type
  317.         //
  318.         this->SetInternalDataLength(t, dataLength);
  319.         this->SetEncodedDataType(t, encodedDataType);
  320.  
  321.         //
  322.         // If the data type could not be encoded into
  323.         // 'encodedDataType', then write the data type
  324.         // into the appropriate record in the property
  325.         // data.
  326.         //
  327.         if(encodedDataType == kEncodedTypedStructure)
  328.             this->SetStoredDataType(t, dataType);
  329.  
  330.         //
  331.         // Data pointer and data mask used in copy loop
  332.         // 
  333.         long* dataPtr = (long*)data.Data();
  334.         long dataMask[] = { 0, 0xFF000000, 0xFFFF0000, 0xFFFFFF00 };
  335.         
  336.         //
  337.         // Write two or three longwords into the property data
  338.         //
  339.         for(short i=0; i<(maxInternalLength >> 2); ++i)
  340.         {            
  341.             //
  342.             // Insure that the internal storage space that is
  343.             // past the end of the internally stored data is
  344.             // always zeroed out.
  345.             //
  346.             if(dataLength <= 0)
  347.                 this->ChangeRecordData(t, kFirstDataWord+i, 0);
  348.             else if(dataLength >= 4)
  349.                 this->ChangeRecordData(t, kFirstDataWord+i, *dataPtr);
  350.             else
  351.                 this->ChangeRecordData(t, kFirstDataWord+i, *dataPtr & dataMask[dataLength]);
  352.             
  353.             ++dataPtr;
  354.             dataLength -= 4;
  355.         }
  356.     }
  357.     else
  358.     {
  359.         //
  360.         // Remember the type of data we're about to save
  361.         //                
  362.         this->SetEncodedDataType(t, kEncodedTypedStructure);
  363.         this->SetStoredDataType(t, dataType);
  364.  
  365.         //
  366.         // Allocate and copy data into an external data record.
  367.         // There may already be an external data record here, or we may
  368.         // need to allocate one.
  369.         //
  370.         AnUpdate<TDataRecord> externalData = this->UpdateExternalData(t);
  371.         if(fExternalData.Exists() == false)
  372.         {
  373.             externalData = this->DBDocument()->NewDataRecord(t, dataLength);
  374.             this->SetOwnsExternalData(t, true);
  375.             externalData->SetDataOwner(t, this);
  376.             fExternalData = externalData;
  377.         }
  378.         
  379.         //
  380.         // Save the data into the external object
  381.         //
  382.         externalData->SetTypedData(t, data);
  383.     }
  384.     
  385.     this->InformOwnerPropertyValueChanged(t);
  386. } // TDBProperty::SetTypedData
  387.  
  388. //--------------------------------------------------------------------------------
  389. // TDBProperty::Verify
  390. //--------------------------------------------------------------------------------
  391. void TDBProperty::Verify(TTransaction* t, Boolean verifyDeep) const
  392. {
  393.     if(this->OwnsExternalData(t))
  394.     {
  395.         AConst<TDataRecord> externalData = this->ExternalData(t);
  396.         
  397.         if(externalData.Exists())
  398.         {
  399.             if(externalData->DataOwnerIndex(t) != this->RecordIndex())
  400.                 DebugStr("\pProperty's external data does not point back");
  401.             externalData->Verify(t, verifyDeep);
  402.         }
  403.         else
  404.             DebugStr("\pProperty claims to own external data but cannot find it");
  405.     }
  406.     
  407.     INHERITED::Verify(t, verifyDeep);
  408. } // TDBProperty::Verify
  409.  
  410. //--------------------------------------------------------------------------------
  411. // TDBProperty::ExternalData
  412. //--------------------------------------------------------------------------------
  413. AConst<TDataRecord> TDBProperty::ExternalData(TTransaction* t) const
  414. {
  415.     //
  416.     // Check to see if our external data has moved
  417.     // out from under us (as it will durring PrepareToCommit
  418.     // if the external data block needs to change size).
  419.     //
  420.     if((fExternalData.Exists() == true) && ((this->OwnsExternalData(t) == false) || (fExternalData->RecordIndex() != this->GetExternalDataIndex(t))))
  421.     {
  422.         ((TDBProperty*)this)->fExternalData = AConst<TDataRecord>(nil);
  423.     }
  424.     
  425.     //
  426.     // If we don't have a cached reference to our external
  427.     // data, and we do own extrnal data, then look up the
  428.     // external data cursor and cache it.
  429.     //
  430.     if((fExternalData.Exists() == false) && (this->OwnsExternalData(t)))
  431.     {
  432.         ((TDBProperty*)this)->fExternalData = this->GetDataCursor(this->GetExternalDataIndex(t));
  433.     }
  434.     
  435.     return fExternalData;
  436. } // TDBProperty::ExternalData
  437.  
  438. //--------------------------------------------------------------------------------
  439. // TDBProperty::UpdateExternalData
  440. //--------------------------------------------------------------------------------
  441. AnUpdate<TDataRecord> TDBProperty::UpdateExternalData(TTransaction* t)
  442. {
  443.     AnUpdate<TDataRecord> externalData;
  444.     
  445.     AConst<TDataRecord> constExternalData = this->ExternalData(t);
  446.     if(constExternalData.Exists())
  447.         externalData = this->Transaction()->GetDataRecordUpdatePointer(constExternalData);
  448.  
  449.     return externalData;
  450. } // TDBProperty::UpdateExternalData
  451.  
  452. //--------------------------------------------------------------------------------
  453. // TDBProperty::SetExternalDataIndex
  454. //--------------------------------------------------------------------------------
  455. void TDBProperty::SetExternalDataIndex(TTransaction* t, long externalDataIndex)
  456. {
  457.     this->ChangeRecordData(t, kExternalDataIndex, externalDataIndex);
  458. } // TDBProperty::SetExternalDataIndex
  459.  
  460. //--------------------------------------------------------------------------------
  461. // TDBProperty::RecordCanHaveElements
  462. //--------------------------------------------------------------------------------
  463. Boolean TDBProperty::RecordCanHaveElements(TTransaction* t) const
  464. {
  465.     //
  466.     // Properties of type kSetOfElements have elements; all
  467.     // other properties cannot have elements
  468.     //
  469.     return (this->GetEncodedDataType(t) == kSetOfElements);
  470. } // TDBProperty::RecordCanHaveElements
  471.  
  472. //--------------------------------------------------------------------------------
  473. // TDBProperty::RecordCanHaveProperties
  474. //--------------------------------------------------------------------------------
  475. Boolean TDBProperty::RecordCanHaveProperties(TTransaction* t) const
  476. {
  477.     //
  478.     // Properties of type kSetOfProperties have properties; all
  479.     // other properties cannot have properties
  480.     //
  481.     return (this->GetEncodedDataType(t) == kSetOfProperties);
  482. } // TDBProperty::RecordCanHaveProperties
  483.  
  484. //--------------------------------------------------------------------------------
  485. // TDBProperty::RemoveFromTree
  486. //
  487. // Properties notify their owner when they are removed from the tree
  488. //--------------------------------------------------------------------------------
  489. void TDBProperty::RemoveFromTree(TTransaction* t)
  490. {
  491.     AConst<TDBRecord> treeOwner = this->TreeOwner(t);
  492.     
  493.     INHERITED::RemoveFromTree(t);
  494.     
  495.     if(treeOwner.Exists())
  496.     {
  497.         (this->Transaction()->GetDBRecordUpdatePointer(treeOwner))->PropertyValueChanged(t, AConst<TDBProperty>(this));
  498.     }
  499. } // TDBProperty::RemoveFromTree
  500.  
  501. //--------------------------------------------------------------------------------
  502. // TDBProperty::InformOwnerPropertyValueChanged
  503. //
  504. // This method is called whenever someone sets the value of a property; its main
  505. // use is for the element record to resort itself if its name property changes.
  506. //--------------------------------------------------------------------------------
  507. void TDBProperty::InformOwnerPropertyValueChanged(TTransaction* t) const
  508. {
  509.     AConst<TDBRecord> treeOwner = this->TreeOwner(t);
  510.     
  511.     if(treeOwner.Exists())
  512.     {
  513.         (this->Transaction()->GetDBRecordUpdatePointer(treeOwner))->PropertyValueChanged(t, AConst<TDBProperty>(this));
  514.     }
  515. } // TDBProperty::InformOwnerPropertyValueChanged
  516.  
  517. //--------------------------------------------------------------------------------
  518. // TDBProperty::PropertyDataReference
  519. //
  520. // Private--only used to do comparisons between properties
  521. //--------------------------------------------------------------------------------
  522. TConstDataReference TDBProperty::PropertyDataReference(TTransaction* t) const
  523. {
  524.     if(this->OwnsExternalData(t) == false)
  525.     {
  526.         return this->RecordDataReference(t, this->GetDataType(t), kFirstDataWord, this->GetDataLength(t));
  527.     }
  528.     else
  529.     {
  530.         AConst<TDataRecord> externalData = this->ExternalData(t);
  531.         Require(externalData.Exists());
  532.         return externalData->DataReference(t);
  533.     }
  534. } // TDBProperty::PropertyDataReference
  535.  
  536. //--------------------------------------------------------------------------------
  537. // TDataRecordComparisonObject::TestObject
  538. //--------------------------------------------------------------------------------
  539. CompareEnumeration TDataRecordComparisonObject::TestObject(TTransaction* t, AConst<TDBRecord> testObject)
  540. {
  541.     return TDBRecord::DetermineCompareEnumeration(fSearchKey, testObject->DBPropertyCursor()->PropertyID(t));
  542. } // TDataRecordComparisonObject::TestObject
  543.